#include "stm32f10x.h"                  // Device header

void AD_Init(void)
{
	// 第一步: 开启RCC时钟: ADC1 和 PA
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	
	// 第二步: 配置 ADDCLK 的分频器。  72M / 6 = 12
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	// 第三步: 配置对应的 GPIO，把需要用的GPIO配置成模拟输入的模式。
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;           // 模拟输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;               // 选择引脚: PA0~PA7
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;       // 输出速度没有特殊要求选择 50MHz 即可
	GPIO_Init(GPIOA, &GPIO_InitStructure);                  // 初始化 A 分组下的0引脚	
	
	// 第四步: 配置多路开关，将左边的通道接入到右边的规则组列表里面。（将通道写入到列表里面）
	// 参数1: 具体ADC外设
	// 参数2: 通道编号
	// 参数3: 列表当中位序, 编号从 1~16
	// 参数4: 采样时间, 看笔记当中的描述, 配置越短越快, 配置的越长越稳定
	// 在规则组菜单列表的第一个位置写入通道0这个通道
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
	
	// 第四步: 结构体初始化 配置 ADC 转换器：ADC 单次转换还是连续转换，扫描还是非扫描，有几个通道，触发源是什么，是左对齐还是右对齐。
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;      // 配置为独立工作模式还是双 ADC 模式
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;  // 数据对齐
#if CONTINUE_NOSCAN
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;     // 连续转换还是单个转换
#else
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;      // 连续转换还是单个转换
#endif
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 外部触发源, 不需要
	ADC_InitStructure.ADC_NbrOfChannel = 1;                 // 在指定扫描模式下, 总共会用到几个通道
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;           // 扫描模式还是非扫描模式
	ADC_Init(ADC1, &ADC_InitStructure);
	
	// 第五步: 开关控制：ADC_Cmd 开启ADC。
	ADC_Cmd(ADC1, ENABLE);
	
	// 第六步: 对 ADC 进行一下校准，这样可以减小误差。
	ADC_ResetCalibration(ADC1);                                    // 复位校准, 软件设置对应标志位, 复位校准完成会被硬件清除
	while(ADC_GetResetCalibrationStatus(ADC1) == SET)              // 获取复位校准状态
	;
	ADC_StartCalibration(ADC1);                                    // 开始校准
	while(ADC_GetCalibrationStatus(ADC1) == SET)                   // 获取开始校准状态
	;
	
#if CONTINUE_NOSCAN
	// 如果定义了连续非扫描模式, 则在ADC初始化的时候只用触发一次转换动作即可
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
#endif
}

uint16_t AD_GetValue(void)
{
#if !CONTINUE_NOSCAN
	// 1. 软件触发转换
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);

	// 2. 等待转换完毕, EOC 只是规则组的完成标志位, 在读取规则组数据寄存器的时候自动清除
	// 等待时间为前面配置的 55.5 采样周期 + 12.5 固定的转换周期, 因此总共是 68 个周期
	// 12MHz => 1/12 us * 68 = 5.6us
	while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET)
	;
#endif
	
	// 3. ADC 获取转换值并返回
	return ADC_GetConversionValue(ADC1);
}